package com.test.a21;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockPart;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver;

 

public class A21 {

	
	public static void main(String[] args) throws Exception {
		/*
		 * ObjectMapper mapper =new ObjectMapper(); User user=new User();
		 * user.setName("张三"); user.setAge(18);
		 * System.out.println(mapper.writeValueAsString(user));
		 */ 
       test3();
	}
	
	
	/*****
	 * 使用解析器组合来解析
	 * RequestParamMethodArgumentResolver 解析器可以出现两次  根据参数来处理不同类型的参数   比如false 表示必须有 @RequestParam    true表示非必须有   可以解析 String name2
	 * ServletModelAttributeMethodProcessor  false 必须有 @ModelAttribute  true表示不必有
	 * 解析器有的位置需要特殊固定下
	 * 
	 * @throws Exception
	 */
	public static void test3() throws Exception {
		 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
	        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
	        // 准备测试 Request
	        HttpServletRequest request = mockRequest();

	        // 要点1. 控制器方法被封装为 HandlerMethod  自定义的控制器类Controller的test方法
	        HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
	       
	     // 要点2. 准备对象绑定与类型转换
	        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
	        
	        // 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
	        ModelAndViewContainer container = new ModelAndViewContainer(); 
	        // 要点4. 解析每个参数值
	        for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
	        	  String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
		            String str = annotations.length() > 0 ? " @" + annotations + " " : " ";  //得到方法的注解
		            parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());  //得到参数名解析器 否则参数名得不到
	        	
	        	  // 多个解析器组合
	              HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
	              composite.addResolvers(
	            		  new RequestParamMethodArgumentResolver(beanFactory, false),
	            		   //beanFactory这个参数可以把${JAVA_HOME}解析    false 表示必须有 @RequestParam    true表示非必须有   可以解析 String name2
	            		  new PathVariableMethodArgumentResolver(),//解析@PathVariable
	            		  new RequestHeaderMethodArgumentResolver(beanFactory),  //解析@RequestHeader
	            		  new ServletCookieValueMethodArgumentResolver(beanFactory), //@CookieValue
	            		  new ExpressionValueMethodArgumentResolver(beanFactory),  //@Value
	            		  new ServletRequestMethodArgumentResolver(), //解析HttpServletRequest   点击进去查看supportsParameter方法可以看支持解析哪些参数类型
	            		  new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
	            		  new RequestResponseBodyMethodProcessor(Arrays.asList(new MappingJackson2HttpMessageConverter())),//解析RequestBody 
	            		  new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute  RequestResponseBodyMethodProcessor需要再ServletModelAttributeMethodProcessor上边
	            		  new RequestParamMethodArgumentResolver(beanFactory, true) // 省略 @RequestParam  位置需要固定下
	            		  
	            		  );
	        	
	        	if(composite.supportsParameter(parameter)) { //判断参数是否支持RequestParamMethodArgumentResolver
	        	 	Object v = composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory);//factory  对象和类型绑定   解析@RequestParam("age") int age
 	                System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v+","+v.getClass());
	        	}else {
	        		 System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() );
	 	            //得到参数下表  参数的注解  参数的类型   参数名
	        	}
	        	
	          
	           
	        }
	}
	
	/****
	 * RequestParamMethodArgumentResolver解析RequestParam
	 * @throws Exception
	 */
	public static void test2() throws Exception {
		 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
	        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
	        // 准备测试 Request
	        HttpServletRequest request = mockRequest();

	        // 要点1. 控制器方法被封装为 HandlerMethod  自定义的控制器类Controller的test方法
	        HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
	       
	     // 要点2. 准备对象绑定与类型转换
	        ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
	        
	        // 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
	        ModelAndViewContainer container = new ModelAndViewContainer(); 
	        // 要点4. 解析每个参数值
	        for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
	        	  String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
		            String str = annotations.length() > 0 ? " @" + annotations + " " : " ";  //得到方法的注解
		            parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());  //得到参数名 否则参数名得不到
	        	
	        	  RequestParamMethodArgumentResolver resolver  =new RequestParamMethodArgumentResolver(beanFactory, true); 
	        	  //beanFactory这个参数可以把${JAVA_HOME}解析    false 表示必须有 @RequestParam    true表示非必须有   可以解析 String name2
	        	
	        	if(resolver.supportsParameter(parameter)) { //判断参数是否支持RequestParamMethodArgumentResolver
	        		Object v = resolver.resolveArgument(parameter, container, new ServletWebRequest(request), factory);//factory  对象和类型绑定   解析@RequestParam("age") int age
	                System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v+","+v.getClass());
	        	}else {
	        		 System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() );
	 	            //得到参数下表  参数的注解  参数的类型   参数名
	        	}
	        	
	          
	           
	        }
	}
	
	
	/****
	 * 参数准备  得到Controller类下test方法的 得到参数下表  参数的注解  参数的类型   参数名
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 */
	public static void test1() throws Exception {
		 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
	        // 准备测试 Request
	        HttpServletRequest request = mockRequest();

	        // 要点1. 控制器方法被封装为 HandlerMethod
	        HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
	        // 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
	        ModelAndViewContainer container = new ModelAndViewContainer(); 
	        // 要点4. 解析每个参数值
	        for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
	            String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
	            String str = annotations.length() > 0 ? " @" + annotations + " " : " ";  //得到方法的注解
	            parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());  //得到参数名 否则参数名得不到
	            System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() );
	            //得到参数下表  参数的注解  参数的类型   参数名
	        }
	}
	
    private static HttpServletRequest mockRequest() {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("name1", "zhangsan");
        request.setParameter("name2", "lisi");
        request.addPart(new MockPart("file", "abc", "hello".getBytes(StandardCharsets.UTF_8)));
        Map<String, String> map = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
       // System.out.println(map);
        request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
        request.setContentType("application/json");
        request.setCookies(new Cookie("token", "123456"));
        request.setParameter("name", "张三");
        request.setParameter("age", "18");
        request.setContent("{\"name\":\"张三\",\"age\":20}".getBytes(StandardCharsets.UTF_8));

        return new StandardServletMultipartResolver().resolveMultipart(request);
    }
	
	static class Controller {
        public void test(
                @RequestParam("name1") String name1, // name1=张三
                String name2,                        // name2=李四
                @RequestParam("age") int age,        // age=18
                @RequestParam(name = "home", defaultValue = "${JAVA_HOME}") String home1, // spring 获取数据
                @RequestParam("file") MultipartFile file, // 上传文件
                @PathVariable("id") int id,               //  /test/124   /test/{id}
                @RequestHeader("Content-Type") String header,
                @CookieValue("token") String token,
                @Value("${JAVA_HOME}") String home2, // spring 获取数据  ${} #{}
                HttpServletRequest request,          // request, response, session ...
                @ModelAttribute("abc") User user1,          // name=zhang&age=18
                User user2,                          // name=zhang&age=18
                @RequestBody User user3              // json
        ) {
        }
    }

    static class User {
        private String name;
        private int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
        }
    }
    
     
}
